
// 字典
function defaultToString(item) {
  if (item === null) {
    return "NULL"
  } else if (item === undefined) {
    return 'UNDEFINED'
  } else if (typeof item === 'string' || item instanceof String) {
    return `${item}`
  }
  return item.toString()
}
class ValuePair {
  constructor(key, value) {
    this.key = key;
    this.value = value
  }
  toString() {
    return `[${this.key}:${this.value}]`
  }
}
class Dictionary {
  constructor(toStrFn = defaultToString) {
    this.toStrFn = toStrFn;
    this.table = {}
  }
  hasKey(key) {
    return this.table[this.toStrFn(key)] != null
  }
  set(key, value) {
    if (key != null && value != null) {
      let tableKey = this.toStrFn(key)
      this.table[tableKey] = new ValuePair(key, value)
      return true
    } else {
      return false
    }
  }
  remove(key) {
    if (this.hasKey(key)) {
      delete this.table[key]
      return true
    } else {
      return false
    }
  }
  get(key) { // 更具key获取对应value
    let tableKey = this.toStrFn(key);
    return this.table[tableKey] != null ? this.table[tableKey].value : undefined
  }
  keyValues() { // 以数组形式返回字典中的所有 valuePair 对象
    // 第一种方法return Object.values(this.table)
    // 第二种方法
    const valuePairs = []
    for (let i in this.table) {
      if (this.hasKey(i)) {
        valuePairs.push(this.table[i])
      }
    }
    return valuePairs

  }
  keys() {
    return this.keyValues().map(item => item.key)
  }
  values() {
    return this.keyValues().map(item => item.value)
  }
  forEach(callback) {
    let valuePairs = this.keyValues()
    for (let i = 0; i < valuePairs.length; i++) {
      let result = callback(valuePairs[i].key, valuePairs[i].value)
      if (result === false) {
        break
      }
    }
  }
  size() {
    return Object.keys(this.table).length
  }
  isEmpty() {
    return this.size() === 0
  }
  clear() {
    this.table = {};
  }
  toString() {
    if (this.isEmpty()) {
      return '';
    }
    const valuePairs = this.keyValues();
    let objString = `${valuePairs[0].toString()}`; // {1} 
    for (let i = 1; i < valuePairs.length; i++) {
      objString = `${objString},${valuePairs[i].toString()}`; // {2} 
    }
    return objString; // {3} 
  }
}

const dictionary = new Dictionary();
dictionary.set('Gandalf', 'gandalf@email.com');
// dictionary.set('John', 'johnsnow@email.com');
// dictionary.set('Tyrion', 'tyrion@email.com');

// console.log(dictionary.values());
// console.log(dictionary.get('Tyrion'));
// dictionary.remove('John');
// console.log(dictionary.keys());
// console.log(dictionary.values());
// console.log(dictionary.keyValues());
// dictionary.forEach((k, v) => {
//   console.log('forEach: ', `key: ${k}, value: ${v}`);
// });

//2 .散列表 HashTable
class HashTable {
  constructor(toStrFn = defaultToString) {
    this.toStrFn = toStrFn
    this.table = {}
  }
  loseloseHashCode(key) {
    if (typeof key === 'number') {
      return key
    }
    let strKey = this.toStrFn(key)
    let hash = 0
    for (let i = 0; i < strKey.length; i++) {
      //charCodeAt() 方法可返回指定位置的字符的 Unicode 编码
      hash += strKey.charCodeAt(i)
    }
    return hash % 37
  }
  hashCode(key) {
    return this.loseloseHashCode(key)
  }
  put(key, value) {
    if (key != null && value != null) {
      let hashKey = this.hashCode(key);
      this.table[hashKey] = new ValuePair(key, value)
      return true
    }
    return false
  }
  get(key) {
    let hashKey = this.hashCode(key);
    return this.table[hashKey] == null ? undefined : this.table[hashKey].value
  }
  delete(key) {
    let hashKey = this.hashCode(key);
    if (this.table[hashKey] != null) {
      delete this.table[hashKey]
      return true
    } else {
      return false
    }
  }
}

let hashTable = new HashTable()
hashTable.put('a', 'a.com')
hashTable.put('b', 'b.com')
console.log(hashTable)
// console.log(hashTable.get('a'))
console.log(hashTable.delete('a'))
console.log(hashTable.get('a'))
console.log(hashTable)